NSZombieEnabled
When
an object is deallocated, if there are any objects with a reference to
the deallocated object, they are no longer referencing a valid object.
Any messages sent to the deallocated object result in errors. Often, the
error is rather cryptic. For instance, the following code fragment is
obviously an error.
FooBar * myFooBar = [[FooBar alloc] init];
NSMutableArray *myArray = [[NSMutableArray alloc] initWithObjects:
myFooBar,nil];
[myFooBar dealloc];
[[myArray objectAtIndex:0] sayHello];
FooBar
is allocated, initialized, and added to myArray. There are two
references to myFooBar, so its retainCount is two. However, deallocating
myFooBar makes both references invalid, pointing to deallocated memory
space. The sayHello message is sent to the first object in myArray—the
problem is that the object no longer exists. Although in this simple
example it is easy enough to surmise the cause of the error message, in a
real application, finding this type of error’s source is often
difficult.
objc[1289]: FREED(id): message sayHello sent to freed
object=0x521a90 Program received signal: "EXC_BAD_INSTRUCTION".
Zombies help avoid this
nebulous error, helping you track down an error’s source. You enable
zombies by setting the NSZombieEnabled environment variable in Xcode.
Then, when debugging the application, rather than releasing an object,
the debugger creates a zombie object. The zombie knows its original
identity before joining the undead. The result is that you usually
receive a more descriptive error message.
2009-02-28 12:28:38.749 Zombie[1316:20b] *** -[FooBar sayHello]:
message sent to deallocated instance 0x52c6a0
Again, in this simple
example, the difference is trivial; in a real-world project, the
difference is not trivial. The following task illustrates using
NSZombieEnabled.
Create a new View-based Application named Zombie. Create a new Objective-C class called FooBar. Create one method called helloThere (Listing 1). Don’t forget to put the method’s signature in FooBar’s interface (Listing 2). Listing 1. FooBar.m
#import "FooBar.h" @implementation FooBar -(void) sayHello { NSLog(@"Hello there..."); } -(void) dealloc { [super dealloc]; } @end
|
Listing 2. FooBar.h
@interface FooBar : NSObject { } -(void) sayHello; @end
|
Modify application:didFinishLaunchingWithOptions in ZombieAppDelegate (Listing 3). Don’t forget to import FooBar. Listing 3. ZombieAppDelegate.m
#import "ZombieAppDelegate.h" #import "ZombieViewController.h" #import "FooBar.h" @implementation ZombieAppDelegate @synthesize window; @synthesize viewController; - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { FooBar * myFooBar = [[FooBar alloc] init]; NSMutableArray * myArray = [[NSMutableArray alloc] initWithObjects:myFooBar,nil]; [myFooBar dealloc]; [[myArray objectAtIndex:0] sayHello];
[window addSubview:viewController.view]; [window makeKeyAndVisible]; return YES; }
- (void)applicationWillTerminate:(UIApplication *)application { // Save data if appropriate. }
-(void)dealloc { [viewController release]; [window release]; [super dealloc]; } @end
|
Click
the Run button to build and debug the application. Notice the first
time you run the application you may not get an error at all (even
though we sent a message to a deallocated object). This is part of what
makes finding and fixing errors like this so hard—if the memory didn’t
happen to get used for anything else in the meantime, it might remain
valid for an unpredictable amount of time. Even if you do get an error
message, it is not all that descriptive (Listing 4).
The debugger only knows the sayHello message was sent to an object
already freed. The debugger doesn’t know the object’s identity. To
change this, you must enable zombies. Listing 4. Debugger Console error logging when zombies are not enabled
Attaching to program: '/Users/bward/Library/Application Support/iPhone Simulator/User/Applications/05688B53-AF22-4F21 -95D3AFFE2682A6EA/Zombie.app/Zombie', process 1351. objc[1351]: FREED(id): message sayHello sent to freed object=0x521bd0 Program received signal: "EXC_BAD_INSTRUCTION".
|
In
the pull-down menu next to the Run button, select Edit Active Scheme.
Select Launch in the left column and then click the Arguments tab. This
should let you edit the arguments passed to the app on launch (Figure 11).
Click the + and add the NSZombieEnabled variable to the environment variable list. Assign it the value YES and click OK. Run the app again. Now the error message is more descriptive (Listing 5).
Listing 5. Debugger Console output after zombies are enabled
2010-08-21 15:10:57.345 Zombie[16692:207] *** -[FooBar sayHello]: message sent to deallocated instance 0x592b640
|
|